﻿using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Fighter.ScreenManagers;
using Fighter.GameContent;
using Microsoft.Xna.Framework.Media;
using System.IO;

namespace Fighter.Screens
{
    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    class GameplayScreen : GameScreen
    {
        #region Fields
        ContentManager content;
        SpriteFont gameFont;
        float pauseAlpha;

        // Resources for drawing.
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;
        private Game game;
        private Texture2D heart;

        private SpriteFont hudFont;

        // Meta-level game state.
        private int levelIndex = -1;
        private Level level;

        private int heightWindow;
        private int widthWindow;
        public static Vector2 baseScreenSize = new Vector2(1920, 1080);

        private KeyboardState keyboardState;

        // The number of levels in the Levels directory of our content. We assume that
        // levels in our content are 0-based and that all numbers under this constant
        // have a level file present. This allows us to not need to check for the file
        // or handle exceptions, both of which can add unnecessary time to level loading.
        private const int numberOfLevels = 3;

        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public GameplayScreen(Game game, GraphicsDeviceManager graphics, int level = 1)
        {
            this.game = game;
            this.graphics = graphics;
            StateManager.CurrentScreenState = StateManager.ScreenState.Ingame;
        }


        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            //Initialisation
            heightWindow = graphics.GraphicsDevice.PresentationParameters.BackBufferHeight;
            widthWindow = graphics.GraphicsDevice.PresentationParameters.BackBufferWidth;
            this.graphics.IsFullScreen = false;
            this.graphics.PreferredBackBufferWidth = widthWindow;
            this.graphics.PreferredBackBufferHeight = heightWindow;
            this.graphics.ApplyChanges();

            this.game.Window.Title = "Fighter";
            this.game.Window.AllowUserResizing = true;
            //Fin Initialisation

            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

            // Load fonts
            gameFont = content.Load<SpriteFont>("Game/Font/gamefont");
            hudFont = content.Load<SpriteFont>("Game/Font/Hud");

            heart = content.Load<Texture2D>("Sprites/Items/Heart");

            // A real game would probably have more content than this sample, so
            // it would take longer to load. We simulate that by delaying for a
            // while, giving you a chance to admire the beautiful loading screen.
            Thread.Sleep(1000);

            // once the load has finished, we use ResetElapsedTime to tell the game's
            // timing mechanism that we have just finished a very long frame, and that
            // it should not try to catch up.
            ScreenManager.Game.ResetElapsedTime();

            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
            try
            {
                MediaPlayer.IsRepeating = true;
                MediaPlayer.Play(content.Load<Song>("Game/Sounds/Music"));
            }
            catch { }

            LoadNextLevel();
        }


        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            content.Unload();
        }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                       bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, false);

            // Gradually fade in or out depending on whether we are covered by the pause screen.
            if (coveredByOtherScreen)
                pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
            else
                pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);

            if (IsActive)
            {
                // Handle polling for our input and handling high-level input
                HandleInput();

                // update our level, passing down the GameTime along with all of our input states
                level.Update(gameTime, keyboardState);
                if (StateManager.CurrentGameState == StateManager.GameState.Death)
                {
                    LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen(),
                                                               new MainMenuScreen(this.game, this.graphics));
                }
                if (StateManager.CurrentGameState == StateManager.GameState.Next)
                {
                    this.LoadNextLevel();
                }
            }
        }


        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            // Look up inputs for the active player profile.
            int playerIndex = (int)ControllingPlayer.Value;

            KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
            GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];

            // The game pauses either if the user presses the pause button, or if
            // they unplug the active gamepad. This requires us to keep track of
            // whether a gamepad was ever plugged in, because we don't want to pause
            // on PC if they are playing with a keyboard and have no gamepad at all!
            bool gamePadDisconnected = !gamePadState.IsConnected &&
                                       input.GamePadWasConnected[playerIndex];

            if (input.IsPauseGame(ControllingPlayer) || gamePadDisconnected)
            {
                ScreenManager.AddScreen(new PauseMenuScreen(this.game, this.graphics), ControllingPlayer);
            }
        }


        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.Black);

            level.Draw(gameTime, spriteBatch, graphics);

            DrawHud();

            // If the game is transitioning on or off, fade it out to black.
            if (TransitionPosition > 0 || pauseAlpha > 0)
            {
                float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);

                ScreenManager.FadeBackBufferToBlack(alpha);
            }
        }


        #endregion

        #region method

        private void HandleInput()
        {
            // get all of our input states
            keyboardState = Keyboard.GetState();
        }

        private void LoadNextLevel()
        {
            // move to the next level
            levelIndex = (levelIndex + 1) % numberOfLevels;

            // Unloads the content for the current level before loading the next one.
            if (level != null)
                level.Dispose();

            // Load the level.
            string levelPath = string.Format("Content/Level/Level{0}/Structure/level.txt", levelIndex);
            using (Stream fileStream = TitleContainer.OpenStream(levelPath))
                level = new Level(this.game, fileStream, levelIndex, graphics, ControllingPlayer, this.ScreenManager);
        }

        private void ReloadCurrentLevel()
        {
            --levelIndex;
            LoadNextLevel();
        }

        private void DrawHud()
        {
            Vector3 screenScalingFactor;

            float horScaling = (float)graphics.GraphicsDevice.PresentationParameters.BackBufferWidth / GameplayScreen.baseScreenSize.X;
            float verScaling = (float)graphics.GraphicsDevice.PresentationParameters.BackBufferHeight / GameplayScreen.baseScreenSize.Y;

            screenScalingFactor = new Vector3(horScaling, verScaling, 1);
            Matrix globalTransformation = Matrix.CreateScale(screenScalingFactor);

            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default,
                              RasterizerState.CullCounterClockwise, null, globalTransformation);

            Rectangle titleSafeArea = graphics.GraphicsDevice.Viewport.TitleSafeArea;
            Vector2 hudLocation = new Vector2(titleSafeArea.X, titleSafeArea.Y);
            Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f,
                                         titleSafeArea.Y + titleSafeArea.Height / 2.0f);

            //DrawShadowedString(hudFont, "test", hudLocation, Color.Black);
            for (int i = 0; i < level.GetPlayerLives(); i++)
            {
                spriteBatch.Draw(heart, new Vector2(hudLocation.X + heart.Width*i + i, hudLocation.Y), Color.White);
            }

            spriteBatch.End();
        }

        private void DrawShadowedString(SpriteFont font, string value, Vector2 position, Color color)
        {
            spriteBatch.DrawString(font, value, position + new Vector2(1.0f, 1.0f), Color.Black);
            spriteBatch.DrawString(font, value, position, color);
        }

        #endregion
    }
}
